<?php
/**
 * PollingPlanRepo.php
 *
 * @copyright   2023 thingsworld.com.cn - All Rights Reserved
 * @link        http://thingsworld.com.cn/
 * @author      stiffer.chen <chenl@thingsworld.com.cn>
 * @created     2023-09-06 15:43:39
 * @modified    2023-09-06 15:43:39
 *
 */

namespace App\Repositories;

use App\Http\Resources\PollingPlanLogResource;
use App\Http\Resources\PollingPlanResource;
use App\Models\Customer;
use App\Models\PollingPlan;
use App\Models\UserModel;
use App\Services\UserService;
use Carbon\Carbon;
use Illuminate\Support\Str;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;

class PollingPlanRepo
{
    /**
     * 未开始
     *  创建的默认状态
     * 进行中:
     *  未开始 -> 开始: 自动触发
     *  暂停 -> 开始: 手动触发
     * 暂停
     *  手动触发
     * 结束
     *  自动触发
     */
    const STATUS_CREATE = 'create';
    const STATUS_CREATE_TEXT = '未开始';
    const STATUS_START = 'start';
    const STATUS_START_TEXT = '进行中';
    const STATUS_PAUSE = 'pause';
    const STATUS_PAUSE_TEXT = '已暂停';
    const STATUS_COMPLETE = 'complete';
    const STATUS_COMPLETE_TEXT = '已结束';

    const CIRCLE_UINT_DAY = 'day';
    const CIRCLE_UNIT_DAY_TEXT = '日';

    const CIRCLE_UNIT_WEEK = 'week';
    const CIRCLE_UNIT_WEEK_TEXT = '周';
    const CIRCLE_UNIT_MONTH = 'month';
    const CIRCLE_UNIT_MONTH_TEXT = '月';

    const LOG_STATUS_SUCCESS = 'success';
    const LOG_STATUS_SUCCESS_TEXT = '成功';

    public static function params()
    {
        $status = [
            ['code' => self::STATUS_CREATE, 'text' => self::STATUS_CREATE_TEXT],
            ['code' => self::STATUS_START, 'text' => self::STATUS_START_TEXT],
            ['code' => self::STATUS_PAUSE, 'text' => self::STATUS_PAUSE_TEXT],
            ['code' => self::STATUS_COMPLETE, 'text' => self::STATUS_COMPLETE_TEXT],
        ];

        $circleUnit = [
            ['code' => self::CIRCLE_UINT_DAY, 'text' => self::CIRCLE_UNIT_DAY_TEXT],
            ['code' => self::CIRCLE_UNIT_WEEK, 'text' => self::CIRCLE_UNIT_WEEK_TEXT],
            ['code' => self::CIRCLE_UNIT_MONTH, 'text' => self::CIRCLE_UNIT_MONTH_TEXT],
        ];

        return [
            'status' => $status,
            'circle_unit' => $circleUnit
        ];
    }

    public static function logs($id)
    {
        self::checkExist($id);
        $limit = intval(request('limit', ''));
        if ($limit < 1) {
            $limit = 10;
        }

        $builder = PollingPlan\Log::query()
            ->where('polling_plan_id', '=', $id)
            ->orderByDesc('create_time');
        $builder->with(['polling_plan', 'polling']);

        $logs = $builder->paginate($limit);
        $logs->transform(function ($item) {
            return new PollingPlanLogResource($item);
        });
        return $logs;
    }

    public static function getList()
    {
        $status = trim(request('status', ''));

        $params = collect(self::params()['status'])->pluck('code')->toArray();
        if (!in_array($status, $params)) {
            $status = '';
        }
        $keyword = trim(request('keyword', ''));
        $planStartAt = trim(request('plan_start_at', ''));
        if ($planStartAt) {
            try {
                $planStartAt = Carbon::parse($planStartAt)->toDateString();
            } catch (\Exception $e) {
                $planStartAt = '';
            }
        }
        $planEndAt = trim(request('plan_end_at', ''));
        if ($planEndAt) {
            try {
                $planEndAt = Carbon::parse($planEndAt)->toDateString();
            } catch (\Exception $e) {
                $planEndAt = '';
            }
        }

        if ($planStartAt) {
            $planStartAt = $planStartAt . ' 00:00:00';
        }
        if ($planEndAt) {
            $planEndAt = $planEndAt . ' 23:59:59';
        }

        $builder = PollingPlan::query()
            ->where('mark', 1)
            ->orderByDesc('update_time')
            ->orderByDesc('create_time');

        if ($status) {
            $builder->where('status', $status);
        }

        if ($keyword) {
            $builder->where(function ($query) use ($keyword) {
                $query->where('number', 'like', "%{$keyword}%")
                    ->orWhere('name', 'like', "%{$keyword}%");
            });
        }

        if ($planStartAt) {
            $builder->where('plan_start_at', '>=', $planStartAt);
        }
        if ($planEndAt) {
            $builder->where('plan_end_at', '>=', $planEndAt);
        }

        $limit = intval(request('limit', ''));
        if ($limit < 1) {
            $limit = 10;
        }

        $builder->with(['logs']);

        $export = intval(request('export', ''));
        if ($export) {
            return self::exportData($builder);
        }

        $result = $builder->paginate($limit);
        $result->transform(function ($item) {
            return new PollingPlanResource($item);
        });
        return $result;
    }

    private static function exportData($builder)
    {
        $idStr = trim(request('ids', ''));
        $idParam = [];
        if ($idStr) {
            $idArr = explode(',', $idStr);
            foreach ($idArr as $item) {
                $item = intval($item);
                if ($item > 0) {
                    $idParam[] = $item;
                }
            }
        }
        if ($idParam) {
            $builder->whereIn('id', $idParam);
        }

        $data = $builder->get()->toArray();
        return self::exportPlans($data);
    }

    private static function exportPlans($data)
    {
        $pathPrefix = 'downloads/polling_polling/' . date('Y/m/d/');
        $prefix = public_path() . '/' . $pathPrefix;
        $excelFileName = 'polling-plan-' . md5(date('YmdHis') . microtime()) . '.xlsx';

        if (!is_dir($prefix)) {
            mkdir($prefix, 0777, true);
        }
        $excelFilePath = $prefix . $excelFileName;
        $spreadsheet = new Spreadsheet();
        $sheet = new Worksheet($spreadsheet, "巡检计划");
        $spreadsheet->addSheet($sheet);
        $spreadsheet->removeSheetByIndex(0);

        $excelData[] = [
            '序号',
            '计划编号',
            '计划名称',
            '客户',
            '联系人',
            '电话',
            '客户地址',
            '计划开始时间',
            '计划截止时间',
            '负责人',
            '创建人',
            '重复周期',
            '计划状态',
            '创建时间'
        ];

        foreach ($data as $item) {
            $excelData[] = [
                $item['id'],
                $item['number'],
                $item['name'] ?? '',
                $item['polling_customer_name'],
                $item['polling_customer_user_name'],
                ' ' . $item['polling_customer_user_telephone'],
                $item['polling_customer_address'],
                $item['plan_start_at'],
                $item['plan_end_at'],
                $item['polling_user_name'],
                $item['create_user_name'],
                $item['circle_unit_text'],
                $item['status_text'],
                Carbon::parse($item['create_time'])->toDateTimeString()
            ];
        }

        $sheet->fromArray($excelData);
        $writer = new Xlsx($spreadsheet);
        $writer->save($excelFilePath);
        return env('APP_URL') . '/' . $pathPrefix . $excelFileName;
    }

    public static function getStatusText($status)
    {
        $params = collect(self::params()['status'])->keyBy('code')->toArray();
        return $params[$status]['text'] ?? $status;
    }

    public static function getCircleUnitText($circleUnit)
    {
        $params = collect(self::params()['circle_unit'])->keyBy('code')->toArray();
        return $params[$circleUnit]['text'] ?? $circleUnit;
    }

    public static function destroy($id)
    {
        self::checkExist($id);
        $pollingPlan = PollingPlan::find($id);
        if (!$pollingPlan->btn_delete) {
            throw new \Exception('当前状态不允许删除');
        }
        $pollingPlan->mark = 0;
        $pollingPlan->saveOrFail();
    }

    public static function pause($id)
    {
        self::checkExist($id);
        $pollingPlan = PollingPlan::find($id);
        if (!$pollingPlan->btn_pause) {
            throw new \Exception('当前状态不允许暂停');
        }

        $status = self::STATUS_PAUSE;

        $pollingPlan->status = $status;
        $pollingPlan->saveOrFail();

        $history = [
            'user_id' => registry('user_id'),
            'status' => $status,
            'content' => (registry('user')->realname ?? '') . ' 暂停了巡检任务 #' . $pollingPlan->number
        ];

        self::addHistory($id, $status, $history);
        return self::getDetail($id);
    }

    public static function start($id)
    {
        self::checkExist($id);
        $pollingPlan = PollingPlan::find($id);
        if (!$pollingPlan->btn_start) {
            throw new \Exception('当前状态不允许开始');
        }

        $status = self::STATUS_START;

        $pollingPlan->status = $status;
        $pollingPlan->saveOrFail();

        $history = [
            'user_id' => registry('user_id'),
            'status' => $status,
            'content' => (registry('user')->realname ?? '') . ' 开始了巡检任务 #' . $pollingPlan->number
        ];

        self::addHistory($id, $status, $history);
        return self::getDetail($id);
    }

    public static function store()
    {
        $data = self::storeData(0);
        $main = $data['main'];
        $history = $data['history'];

        $status = $main['status'];

        $pollingPlan = new PollingPlan();
        $pollingPlan->fill($main);
        $pollingPlan->saveOrFail();

        $pollingPlanId = $pollingPlan->id;

        self::addHistory($pollingPlanId, $status, $history);

        return self::getDetail($pollingPlanId);
    }

    public static function update($id)
    {
        self::checkExist($id);
        $data = self::storeData($id);
        $main = $data['main'];
        $history = $data['history'];

        $status = $main['status'];
        $pollingPlan = PollingPlan::find($id);
        $pollingPlan->fill($main);
        $pollingPlan->saveOrFail();

        $pollingPlanId = $pollingPlan->id;

        self::addHistory($pollingPlanId, $status, $history);

        return self::getDetail($pollingPlanId);
    }

    public static function getDetail($id)
    {
        self::checkExist($id);
        $pollingPlan = PollingPlan::find($id);
        return new PollingPlanResource($pollingPlan);
    }

    private static function checkExist($id)
    {
        $pollingPlan = PollingPlan::find($id);
        if (empty($pollingPlan) || !$pollingPlan->mark) {
            throw new \Exception('计划不存在');
        }
    }

    private static function addHistory($id, $status, $historyData)
    {
        $pollingPlan = PollingPlan::find($id);
        if (empty($pollingPlan)) {
            return;
        }
        $pollingPlan->status = $status;
        $pollingPlan->saveOrFail();

        $historyData['polling_plan_id'] = $id;

        $history = new PollingPlan\History();
        $history->fill($historyData);
        $history->saveOrFail();
    }

    private static function storeData($id)
    {
        $userInfo = (new UserService())->getUserInfo(registry('user_id'));
        $isManager = $userInfo['data']['is_manager'] ?? false;
        if (!$isManager) {
            throw new \Exception('仅区域经理可以创建或修改巡检计划');
        }

        if ($id) {
            $pollingPlan = PollingPlan::find($id);
            $number = $pollingPlan->number;
            $status = $pollingPlan->status;
        } else {
            $number = self::getNumber();
            $status = self::STATUS_CREATE;
        }
        $name = trim(request('name', ''));
        if (!$name) {
            throw new \Exception('请输入计划名称');
        }

        $now = Carbon::now()->toDateTimeString();

        $planStartAt = trim(request('plan_start_at', ''));
        if (!$planStartAt) {
            throw new \Exception('请输入计划开始时间');
        }
        try {
            $planStartAt = Carbon::parse($planStartAt)->toDateTimeString();
        } catch (\Exception $e) {
            throw new \Exception('计划开始时间格式错误');
        }

        if ($planStartAt <= $now) {
            // throw new \Exception('计划开始时间需大于当前时间');
        }

        $planEndAt = trim(request('plan_end_at', ''));
        if (!$planEndAt) {
            throw new \Exception('请输入计划截止时间');
        }
        try {
            $planEndAt = Carbon::parse($planEndAt)->toDateTimeString();
        } catch (\Exception $e) {
            throw new \Exception('计划截止时间格式错误');
        }
        if ($planEndAt <= $planStartAt) {
            throw new \Exception('计划截止时间需大于计划开始时间');
        }
        $circleUnit = trim(request('circle_unit', ''));
        if (!$circleUnit) {
            throw new \Exception('请选择计划周期');
        }

        $params = self::params();
        $circleUnits = collect($params['circle_unit'])->pluck('code')->toArray();

        if (!in_array($circleUnit, $circleUnits)) {
            throw new \Exception('计划周期错误');
        }

        $circleCount = intval(request('circle_count', ''));
        if ($circleCount < 1) {
            throw new \Exception('周期单位内开始时间错误');
        }

        $planAt = trim(request('plan_at', ''));
        if (!$planAt) {
            throw new \Exception('请输入巡检任务计划时间');
        }
        try {
            $planAt = Carbon::parse($planAt)->toTimeString();
        } catch (\Exception $e) {
            throw new \Exception('巡检任务计划时间格式错误');
        }

        $planAt = Carbon::now()->toDateString() . ' ' . $planAt;

        $pollingUserId = intval(request('polling_user_id', ''));
        if (!$pollingUserId) {
            throw new \Exception('请选择任务负责人');
        }
        $pollingUser = UserModel::find($pollingUserId);
        if (empty($pollingUser)) {
            throw new \Exception('任务负责人不存在');
        }

        $pollingCustomerId = intval(request('polling_customer_id', ''));
        if (!$pollingCustomerId) {
            throw new \Exception('请选择任务所属客户');
        }
        $customer = Customer::find($pollingCustomerId);
        if (empty($customer)) {
            throw new \Exception('任务所属客户不存在');
        }
        $pollingCustomerUserId = intval(request('polling_customer_user_id', ''));
        if (!$pollingCustomerUserId) {
            throw new \Exception('请选择任务联系人');
        }
        $customerUser = Customer\User::find($pollingCustomerUserId);
        if (!$customerUser) {
            throw new \Exception('任务联系人不存在');
        }
        if ($customerUser->customer_id != $pollingCustomerId) {
            throw new \Exception('任务联系人非任务客户所属');
        }
        $pollingDescription = trim(request('polling_description', ''));

        $address = $customer->province_name . $customer->city_name . $customer->county_name . $customer->address;

        $data = [
            'number' => $number,
            'name' => $name,
            'status' => $status,
            'plan_start_at' => $planStartAt,
            'plan_end_at' => $planEndAt,
            'circle_unit' => $circleUnit,
            'circle_count' => $circleCount,
            'plan_at' => $planAt,
            'create_user_id' => registry('user_id'),
            'create_user_name' => registry('user')->realname ?? '',
            'polling_user_id' => $pollingUserId,
            'polling_user_name' => $pollingUser->realname ?? '',
            'polling_customer_id' => $pollingCustomerId,
            'polling_customer_name' => $customer->name,
            'polling_customer_user_id' => $pollingCustomerUserId,
            'polling_customer_user_name' => $customerUser->name ?? '',
            'polling_customer_user_telephone' => $customerUser->phone,
            'polling_customer_address' => $address,
            'polling_description' => $pollingDescription
        ];

        $history = [
            'user_id' => registry('user_id'),
            'status' => self::STATUS_CREATE,
            'content' => (registry('user')->realname ?? '') . ' 创建了巡检任务 #' . $number
        ];
        return [
            'main' => $data,
            'history' => $history
        ];
    }

    private static function getNumber()
    {
        $prefix = 'PP_';
        $number = $prefix . date('Ymd') . Str::random(8);
        $numberPolling = PollingPlan::byNumber($number)->first();
        if (empty($numberPolling)) {
            return $number;
        }
        return self::getNumber();
    }
}